//package com.ruoyi.config;
//
//import lombok.extern.slf4j.Slf4j;
//import org.apache.http.HeaderElement;
//import org.apache.http.HeaderElementIterator;
//import org.apache.http.HttpHost;
//import org.apache.http.client.HttpClient;
//import org.apache.http.client.config.CookieSpecs;
//import org.apache.http.client.config.RequestConfig;
//import org.apache.http.client.protocol.HttpClientContext;
//import org.apache.http.conn.ConnectionKeepAliveStrategy;
//import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
//import org.apache.http.impl.client.HttpClientBuilder;
//import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
//import org.apache.http.message.BasicHeaderElementIterator;
//import org.apache.http.protocol.HTTP;
//import org.apache.http.protocol.HttpContext;
//import org.springframework.context.annotation.Bean;
//import org.springframework.context.annotation.Configuration;
//import org.springframework.http.client.BufferingClientHttpRequestFactory;
//import org.springframework.http.client.ClientHttpRequestFactory;
//import org.springframework.http.client.ClientHttpRequestInterceptor;
//import org.springframework.http.client.ClientHttpResponse;
//import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
//import org.springframework.http.client.SimpleClientHttpRequestFactory;
//import org.springframework.http.converter.HttpMessageConverter;
//import org.springframework.http.converter.StringHttpMessageConverter;
//import org.springframework.web.client.DefaultResponseErrorHandler;
//import org.springframework.web.client.RestTemplate;
//
//import javax.annotation.Resource;
//import java.io.IOException;
//import java.nio.charset.Charset;
//import java.nio.charset.StandardCharsets;
//import java.util.ArrayList;
//import java.util.HashMap;
//import java.util.List;
//import java.util.Map;
//import java.util.Optional;
//
//@Slf4j
//@Configuration
//public class RestTemplateConfig {
//
//    /**
//     * 官方推荐使用检查永久链接的可用性,而不推荐每次请求的时候才去检查 (milliseconds 毫秒)
//     */
//    private int connectionValidateAfterInactivityMs = 10 * 1000;
//
//    /**
//     * 连接池的最大连接数
//     */
//    private int maxTotalConnect = 50;
//
//    /**
//     * 同路由的并发数 每个路由的最大连接数,如果只调用一个地址,可以将其设置为最大连接数
//     */
//    private int maxConnectPerRoute = 10;
//
//    /**
//     * 客户端和服务器建立连接超时，默认15s 指客户端和服务器建立连接的超时时间,ms , 最大约21秒,因为内部tcp在进行三次握手建立连接时,默认tcp超时时间是20秒
//     */
//    private int connectTimeout = 20 * 1000;
//
//    /**
//     * 指客户端从服务器读取数据包的间隔超时时间,不是总读取时间,默认20s
//     */
//    private int readTimeout = 20 * 1000;
//
//    /**
//     * 重试次数,默认2次
//     */
//    private int retryTimes = 2;
//
//    /**
//     * 从连接池获取连接的超时时间,不宜过长,单位ms
//     */
//    private int connectionRequestTimout = 200;
//
//    /**
//     * 针对不同的地址,特别设置不同的长连接保持时间,单位s,如果是频繁而持续的请求,可以设置小一点,不建议设置过大,避免大量无用连接占用内存资源
//     */
//    private static Map<String, Integer> keepAliveTargetHost = null;
//
//    /**
//     * 长连接保持时间 单位s,不宜过长
//     */
//    private int keepAliveTime = 10;
//
//    @Resource
//    private RestTemplateLog restTemplateLog;
//
//    /**
//     * 设置httpclient参数
//     */
//    @Bean
//    public HttpClient httpClient(ConnectionKeepAliveStrategy connectionKeepAliveStrategy) {
//        HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
//        // 使用Httpclient连接池的方式配置(推荐)，同时支持netty，okHttp以及其他http框架
//        PoolingHttpClientConnectionManager poolingHttpClientConnectionManager =
//                new PoolingHttpClientConnectionManager();
//        // 最大连接数
//        poolingHttpClientConnectionManager.setMaxTotal(maxTotalConnect);
//        // 同路由并发数
//        poolingHttpClientConnectionManager.setDefaultMaxPerRoute(maxConnectPerRoute);
//        // 官方推荐使用检查永久链接的可用性,而不推荐每次请求的时候才去检查 (milliseconds 毫秒)
//        poolingHttpClientConnectionManager.setValidateAfterInactivity(connectionValidateAfterInactivityMs);
//
//        // 配置连接池
//        httpClientBuilder.setConnectionManager(poolingHttpClientConnectionManager);
//
//        // 重试次数
//        httpClientBuilder.setRetryHandler(new DefaultHttpRequestRetryHandler(retryTimes, true, new ArrayList<>()) {
//            @Override
//            public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {
//                return super.retryRequest(exception, executionCount, context);
//            }
//        });
//
//        // 设置长连接保持策略
//        httpClientBuilder.setKeepAliveStrategy(connectionKeepAliveStrategy);
//
//        /**
//         * 处理Invalid cookie header和Cookie rejected错误，配置Cookie策略为ignoreCookies
//         * cookieSpec:即cookie策略。参数为cookiespecs的一些字段。作用： 1、如果网站header中有set-cookie字段时，采用默认方式可能会被cookie
//         * reject，无法写入cookie。将此属性设置成CookieSpecs.STANDARD_STRICT可避免此情况。
//         * 2、如果要想忽略cookie访问，则将此属性设置成CookieSpecs.IGNORE_COOKIES。
//         */
//        httpClientBuilder
//                .setDefaultRequestConfig(RequestConfig.custom().setCookieSpec(CookieSpecs.IGNORE_COOKIES).build());
//        return httpClientBuilder.build();
//    }
//
//    /**
//     * 创建HTTP客户端工厂
//     */
//    @Bean
//    public ClientHttpRequestFactory clientHttpRequestFactory(HttpClient httpClient) {
//        HttpComponentsClientHttpRequestFactory clientHttpRequestFactory =
//                new HttpComponentsClientHttpRequestFactory(httpClient);
//        // 连接超时
//        clientHttpRequestFactory.setConnectTimeout(connectTimeout);
//        // 数据读取超时时间，即SocketTimeout
//        clientHttpRequestFactory.setReadTimeout(readTimeout);
//        // 从连接池获取请求连接的超时时间，不宜过长，必须设置，比如连接不够用时，时间过长将是灾难性的
//        clientHttpRequestFactory.setConnectionRequestTimeout(connectionRequestTimout);
//        return clientHttpRequestFactory;
//    }
//
//    /**
//     * 设置长连接保持策略
//     */
//    @Bean
//    public ConnectionKeepAliveStrategy connectionKeepAliveStrategy() {
//        return (response, context) -> {
//            // response的header中存在keep-alive字段使用timeout指定的时间
//            HeaderElementIterator it = new BasicHeaderElementIterator(response.headerIterator(HTTP.CONN_KEEP_ALIVE));
//            while (it.hasNext()) {
//                HeaderElement he = it.nextElement();
//                String param = he.getName();
//                String value = he.getValue();
//                if (value != null && "timeout".equalsIgnoreCase(param)) {
//                    try {
//                        return Long.parseLong(value) * 1000;
//                    } catch (NumberFormatException ignore) {
//                        log.error("解析长连接过期时间异常 {}", ignore);
//                    }
//                }
//            }
//            // response没有指定keep-alive时长，使用配置的时长，如果不在配置的ip，使用默认时长
//            HttpHost target = (HttpHost)context.getAttribute(HttpClientContext.HTTP_TARGET_HOST);
//            // 如果请求目标地址,单独配置了长连接保持时间,使用该配置
//            Optional<Map.Entry<String, Integer>> any = Optional.ofNullable(keepAliveTargetHost).orElseGet(HashMap::new)
//                    .entrySet().stream().filter(e -> e.getKey().equalsIgnoreCase(target.getHostName())).findAny();
//            // 否则使用默认长连接保持时间
//            Long aLong = any.map(en -> en.getValue() * 1000L).orElse(keepAliveTime * 1000L);
//            return aLong;
//        };
//    }
//
//    @Bean
//    public RestTemplate restTemplate(ClientHttpRequestFactory clientHttpRequestFactory) {
//        SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
//        // 解决401报错时，报java.net.HttpRetryException: cannot retry due to server authentication, in streaming mode
//        requestFactory.setOutputStreaming(false);
//        RestTemplate restTemplate = new RestTemplate(requestFactory);
//
//        // 添加拦截器
//        List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();
//        interceptors.add(restTemplateLog);
//        restTemplate.setInterceptors(interceptors);
//
//        // 提供对传出/传入流的缓冲,可以让响应body多次读取(如果不配置,拦截器读取了Response流,再响应数据时会返回body=null)
//        restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(clientHttpRequestFactory));
//
//        // 我们采用RestTemplate内部的MessageConverter
//        // 重新设置StringHttpMessageConverter字符集为UTF-8，解决中文乱码问题
//        modifyDefaultCharset(restTemplate);
//
//        // 请求失败异常处理，如果不重写hasError方法，抛出异常，无法执行后续代码
//        restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {
//            @Override
//            public boolean hasError(ClientHttpResponse response) {
//                return false;
//            }
//        });
//        return restTemplate;
//    }
//
//    private void modifyDefaultCharset(RestTemplate restTemplate) {
//        List<HttpMessageConverter<?>> converterList = restTemplate.getMessageConverters();
//        HttpMessageConverter<?> converterTarget = null;
//        for (HttpMessageConverter<?> item : converterList) {
//            if (StringHttpMessageConverter.class == item.getClass()) {
//                converterTarget = item;
//                break;
//            }
//        }
//        if (null != converterTarget) {
//            converterList.remove(converterTarget);
//        }
//        Charset defaultCharset = StandardCharsets.UTF_8;
//        converterList.add(1, new StringHttpMessageConverter(defaultCharset));
//    }
//
//}
//
